#ifndef _BIPED_INSTANCE_H_
#define _BIPED_INSTANCE_H_

#include "bones/biped_anim.h"
#include "ui/ui_sprite.h"
//#define BONES_SINGLE_LAYER
namespace bones
{
// used for communicating 


class	BipedInstance
{
public:
	struct				BoneData
	{
		BoneData() : ID(0), SortL(0), SortR(0), pBone(0), pAttachedBiped(0), IsAdditive(false), IsVisible(true)
		{
			
		}
		~BoneData()
		{
#ifndef BONES_SINGLE_LAYER
			for (unsigned int i=0; i<Sprites.size(); i++) delete Sprites[i];
			Sprites.resize(0);
#endif 
		}
		ui::UISprite*	GetSprite()
		{
#ifdef BONES_SINGLE_LAYER
			return &Sprite;
#else
			if (Sprites.empty()) return NULL;
			return Sprites[0];
#endif //BONES_SINGLE_LAYER
		}

		bool			NoValidSprites()
		{
#ifdef BONES_SINGLE_LAYER
			return !Sprite.IsValid();
#else
			return Sprites.empty();
#endif //BONES_SINGLE_LAYER

		}

		void			AddSprite(const std::string& Name, int InsertIdx = -1);
		int							ID;
		std::string					Name, PostCat;
#ifdef BONES_SINGLE_LAYER
		ui::UISprite				Sprite;
#else
		std::vector<ui::UISprite*>	Sprites;
#endif //BONES_SINGLE_LAYER
		float						SortL, SortR;
		bool						IsAdditive;
		bool						IsVisible;
		BipedBone*					pBone;

	// biped attached to me (for characters, etc)
		bones::BipedInstance*		pAttachedBiped;
		math::Vec2					AttachedBipedOffset;


		static bool					SortNormalCB( BoneData * elem1, BoneData* elem2 )
		{
			return elem1->SortL < elem2->SortL;
		}
		static bool					SortFlippedCB( BoneData * elem1, BoneData* elem2 )
		{
			return elem1->SortR < elem2->SortR;
		}
	};

	BipedInstance();
	~BipedInstance();

	void				FlushImages();

	void				ResetAnimation();	//! Reset to first frame in sequence
	void				Clear();			//! Clear all animation information. Usefull for triggered reload
	bool				Tick(float dt);			// returns FALSE if the animation has finished running
	void				RenderSubset(const math::Vec2& Offset, const std::vector<std::string>& Bones);
	void				Render(const math::Vec2& Offset);
	void				RenderShadow(const math::Vec2& Offset, const math::Vec2& Scale);
	void				RenderDebug(const math::Vec2& Offset);
	bool				SetAnimation(const std::string& Name, bool Force = false);	//! Set a new animation. Use Force to override the fact that it's already there
	bool				LoadSprites(const std::string& File);
	bool				LoadDefaults();		//! reload all sprites from the bone-data. Usually not done to save time, but necessary if the animations change
	void				ReplaceSprite(const std::string& Bone, const std::string& Sprite, int InsertIdx = -1);
	void				AttachSprite(const std::string& Bone, const std::string& Sprite, int InsertIdx = -1);
	int					DeleteSprite(const std::string& Bone, const std::string& Sprite = "");
	void				SetBoneAdditive(const std::string& Bone, bool Additive);
	void				SetBoneVisible(const std::string& Bone, bool Value);
	bool				IsBoneVisible(const std::string& Name) { return IsBoneVisible(GetBone(Name)); };

	void				SetNextAnim(const std::string& Name, bool	 Force = false);
	void				SetPos(const math::Vec2& P) { m_Pos = P; };
	const math::Vec2&	GetPos() const { return m_Pos; };
	bool				GetBonePos(const std::string& Name, math::Vec2& From, math::Vec2& To, math::Vec2& Dir, float& Rotation) { return GetBonePos(GetBone(Name), From, To, Dir, Rotation); };
	bool				GetBonePos(BoneData* pBone, math::Vec2& From, math::Vec2& To, math::Vec2& Dir, float& Rotation);
	bool				GetRawBonePos(const std::string& Name, math::Vec2* pFrom, math::Vec2* pTo = NULL);
	bool				GetRawBonePos(BoneData* pBone,  math::Vec2* pFrom, math::Vec2* pTo);


	bool				IsValid() { return m_pAnimation != NULL; };
	bool				IsInside(const math::Vec2& P);
	bool				IsInsideSprite(const math::Vec2& P);

	bool				IsFlipped() const { return m_Flipped; };
	void				SetFlipped(bool b);

	void				AttachBiped(BipedInstance* pBiped, const std::string& Bone, const math::Vec2& Offset);
	void				DetachBiped(BipedInstance* pBiped);
	void				GetExtents(math::Vec2& Min, math::Vec2& Max);
	bool				GetSpriteExtents(math::Vec2& Min, math::Vec2& Max);

	const std::string&	GetAnimation() const { return m_CurrentAnim; };
	const std::string&	GetNextAnimation() const { return m_NextAnim; };
	float				GetAnimationTime();
	float				GetCurrentAnimationTime(){ return m_AnimTime; };
	void				ForceAnimationTime(float f) { m_AnimTime = f; };

	typedef void (*BipedCB)(BipedInstance*, bones::BipedAnimation*, void*pData);	// Arena callback
	void				SetCompleteCB(BipedCB		pCB,	void* pData) { m_CallbackA[CB_ANIM_COMPLETE].pCBData = pData; m_CallbackA[CB_ANIM_COMPLETE].pCB = pCB; };
	void				SetTransitionCompleteCB(BipedCB		pCB,	void* pData) { m_CallbackA[CB_TRANSITION_COMPLETE].pCBData = pData; m_CallbackA[CB_TRANSITION_COMPLETE].pCB = pCB; };
	void				ClearSprites();
	void				SetSpriteReload(bool b) { m_ForceSpriteReload = b; };
	void				SetReverseAttachedBiped(bool b) { m_ReverseAttachedBiped = b; };


	void						SetWrapAnimation(bool b) { m_WrapAnimation = b; };
	void						SetSpritePost(const std::string& str) { m_SpritePost = str; };
	int							GetFrameID() const { return m_FrameID; };
	const std::string&			GetSpritePost() const { return m_SpritePost; };
	void						SetBonePost(const std::string& Name, const std::string& Data);
	int							GetBoneC() { return (int)m_Bones.size(); };

	bool						IsBoneVisible(BoneData* pBone);
	void						GetBoneData(BoneData* pBone, math::Vec2& P, math::Vec2& Dir);
	void						RenderBone(BoneData* pBone,const math::Vec2& Offset, const math::Vec2& Scale);
	void						RefreshBones();
	void						ResortBones();
	bool						IsVisible(const math::Vec2& Offset);
	BoneData*					GetBone(const std::string& Name);
	BoneData*					GetBone(unsigned int);
	BoneData*					GetBoneByIdx(int	Index);

protected:
	std::vector<BoneData*>		m_Bones;
	math::Vec2					m_Pos;
	bones::BipedAnimation*		m_pAnimation;
	bones::BipedAnimation*		m_pBlendAnimation;
	int							m_FrameID;
	float						m_AnimTime, m_BlendTime;
	std::string					m_CurrentAnim, m_NextAnim;
	std::string					m_SpritePost;
	bool						m_Flipped;
	bool						m_ForceSpriteReload;
	bool						m_ReverseAttachedBiped;
	bool						m_WrapAnimation;


	struct		CallbackData
	{
		CallbackData() : pCBData(0), pCB(0) {};
		void*						pCBData;
		BipedCB						pCB;
	};
	enum						CALLBACKS
	{
		CB_ANIM_COMPLETE,
		CB_TRANSITION_COMPLETE,
		CB_COUNT
	};
	CallbackData				m_CallbackA[CB_COUNT];
public:
	float						OBJECT_SCALE;
	float						FRAME_SPEED;
	bool						FLIP_CHARACTER;
	std::string					DEFAULT_PRECAT;
	COLOR						OBJECT_COLOR;
	bool						IS_ADDITIVE;
	float						SHADOW_SCALE;


	static bool					DRAW_BONES;
	static unsigned int			BIPEDS_DRAWN;
	static unsigned int			BONES_DRAWN;		
	static float				XBLEND_TIME;

};

} // namespace bones

#endif // _BIPED_INSTANCE_H_